console.log("loading: msgbroker.js");
function MSGBroker_Send(path, data, onResultJson) {
	return $.post(path, data, onResultJson, "json");
};

function MessageBroker() {
}

/*
 * init: urlPing,urlAuth,urlPong
 */

MessageBroker.prototype = {
	init : function(params) {
		var mc = this;
		mc._urlPing = params.urlPing;
		mc._urlAuth = params.urlAuth;
		mc._urlPong = params.urlPong;
		mc._urlSLock = params.urlSLock;
		mc._deferredTime = params.deferredTime || 30;
		mc._poolTime = params.poolTime || 10;

		mc._isWS = false;/* WebSocket != undefined; */
		mc._ssid = params.ssid || "";
		mc._isConnected = params.ssid!=undefined;
		mc.subscriptions = [];
		mc._requestId = 0;
		mc._lastPingStamp = 0;
		mc._pingProcessMessageProxy = jQuery.proxy(mc._pingProcessMessage, mc);
		mc.pollProxy = jQuery.proxy(mc.pollEx, mc);
		mc._deferredSendProxy = jQuery.proxy(mc._deferredSend, mc);
		mc._dSend = false;
		mc._deferredMetadata = {};
		mc._deferredMsg = {};
		if (mc._isConnected){
			mc.poll();
		}
		return this;
	},
	setDeferredTime : function(newValue) {
		mc._deferredTime = newValue;
		return this;
	},
	getDeferredTime : function() {
		return mc._deferredTime;
	},
	_onAuthenticate : function(msg) {
		var mc = this;
		if (Error.isError(msg)) {
			if (mc.__onAuthenticate) {
				mc.__onAuthenticate(undefined);
			}
			return;
		}
		mc._isConnected = true;
		mc._ssid = msg.ssid;
		$.post(mc._urlSLock,Messages.Common.sessionLock(mc._ssid,Config.Profile.Locale),function(json){
			Config.Profile.MainAccount = {id:json.id};
			mc.poll();
			if (mc.__onAuthenticate) {
				mc.__onAuthenticate(msg);
			}			
		},"json");
	},
	authenticate : function(login, password, oncallback) {
		var mc = this;
		if (mc._isConnected) {
			console.error('Already connected');
			return;
		}
		mc.__onAuthenticate = oncallback;
		$.post(mc._urlAuth, Messages.Common.authenticate(login, password),function(msg){mc._onAuthenticate(msg);}, "json");
	},

	autoAuthenticate : function(oncallback) {
		var mc = this;

		if (mc._isConnected) {
			console.error('Already connected');
			return;
		}
		mc.__onAuthenticate = oncallback;
		$.getJSON(this._urlAuth, jQuery.proxy(mc._onAuthenticate, mc));
	},
	getSessionId : function() {
		return this._ssid;
	},

	_deferredSend : function() {
		var mc = this;

		$.post(mc._urlPong, Messages.Common.pong(mc._ssid, mc._deferredMsg),
				function(json) {
					if (Error.isError(json)) {
						console.log(Error.getErrorMessages(json));
						mc.disconnect();
						return;
					}

					for ( var i in json) {
						var md = mc._deferredMetadata[i];
						delete mc._deferredMetadata[i];
						try {
							if (md.c) {
								md.c(json[i]);
							}
						} catch (ex) {
							console.error(ex);
						}
					}
				}, "json");
		this._deferredMsg = {};
		this._dSend = false;
	},
	abortRequest : function(target) {
		if (target == undefined) {
			return;
		}
		if (target.abort) {
			target.abort();
		} else {
			var rId = target.rid;
			delete this._deferredMsg[rId];
			delete this._deferredMetadata[rId];
		}
	},
	sendMessage : function(msg, callback) {
		var mc = this;
		if (!mc._isConnected) {
			return {
				rId : -1
			};
		}
		if (mc._dSend != true) {
			mc._dSend = true;
			setTimeout(mc._deferredSendProxy, mc._deferredTime);
		}
		var rId = mc._requestId++;
		mc._deferredMsg[rId] = $.toJSON(msg);
		mc._deferredMetadata[rId] = {
			c : callback,
			date : new Date()
		};
		return {
			rId : rId
		};
	},

	pollEx : function() {
		try {
			// ping the server with last message stamp
			var mc = this;
			$.post(mc._urlPing/*"?"+mc._requestId++*/, Messages.Common
					.ping(mc._ssid, mc._lastPingStamp),
					mc._pingProcessMessageProxy,"json");
			// erase the stamp
			mc._lastPingStamp = 0;
		} catch (ex) {
			// setTimeout(mc.pollProxy, 1000);
			console.error(ex);
		}
	},
	// polls the server for new messages
	poll : function() {
		setTimeout(this.pollProxy, this._poolTime);
	},

	distributeToSubscribers : function(msg) {
		var subscription = this.subscriptions[msg.t];
		if (subscription) {
			for ( var curr in subscription) {
				if (subscription.hasOwnProperty(curr)) {
					var evalStruct = subscription[curr];
					if (evalStruct.evaluator(msg.d) != undefined) {
						var callbacks = evalStruct.callbacks;
						for ( var i = 0, l = callbacks.length; i < l; i++) {
							callbacks[i](msg.d);
						}
					}
				}
			}
		}
	},
	// used to process internal messages (pings, reconnect, etc.)
	processServiceMessage : function(msg) {
		if (msg.t == 'ping') {
			this._lastPingStamp = msg.d.stamp;
			return true;
		}
	},
	_pingProcessMessage : function(msgs) {
		if (Error.isError(msgs)) {
			console.log(Error.getErrorMessages(msgs));
			this.disconnect();
			return;
		}

		for ( var i = 0, l = msgs.length; i < l; i++) {
			var msg = msgs[i];
			// if this message is a service we process it in this method and
			// quit
			if (this.processServiceMessage(msg)) {
				continue;
			}
			// do the processing of the subscriptions
			this.distributeToSubscribers(msg);
		}
		// request new messages from the server
		this.poll();
	},

	// subscribe is used to subscribe a callback function
	// to all messages of a particular type satisfying a certain
	// criteria. Use criteria set to true to implicitly subscribe to all
	// messages with a
	// of a certain type
	// msgType - string
	// criteria - string-based expression that can be evaluated - TBD
	// callback - handler function reference with the following (desired)
	// notation
	// function exampleCallback(msg){ // msg - the incoming message body
	// return true; // important note: true here means that the event was
	// processed by the callback
	// }
	subscribe : function(msgType, criteria, callback) {
		var subscription = this.subscriptions[msgType];
		if (subscription == undefined) {
			subscription = [];
			this.subscriptions[msgType] = subscription;
		}
		var evalStruct = subscription[criteria];
		if (evalStruct == undefined) {
			evalStruct = {
				evaluator : this.createEvaluator(criteria),
				callbacks : []
			};
			subscription[criteria] = evalStruct;
		}
		var tmp = evalStruct.callbacks;
		for ( var cbi = 0; cbi < tmp.length; cbi++) {
			if (tmp[cbi] == callback) {
				return this;
			}
		}
		tmp.push(callback);
		return this;
	},

	createEvaluator : function(criteria) {
		eval('var tmpEval = function(data) { return (data.' + criteria + ');}');
		return tmpEval;
	},

	// unsubscribe is used to unsubscribe a callback function
	// from all messages of a particular type satisfying a certain
	// criteria
	// msgType - string
	// criteria - string-based expression that can be evaluated
	// callback - handler function reference
	unsubscribe : function(msgType, criteria, callback) {
		var subscription = this.subscriptions[msgType];
		if (subscription == undefined) {
			return this;
		}
		var evalStruct = subscription[criteria];
		if (evalStruct == undefined) {
			return this;
		}
		var tmp = evalStruct.callbacks;
		for ( var cbi = 0; cbi < tmp.length; cbi++) {
			if (tmp[cbi] == callback) {
				tmp.splice(cbi, 1);
				if (tmp.length == 0) {
					/* Нет больше подписчиков */
					delete subscription[criteria];
				}
				return this;
			}
		}
		return this;
	},
	disconnect : function() {
		this._isConnected = false;
		return this;
	}
};
